/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server.network;

import java.io.*;
import java.net.*;
import com.cse.p2p.common.*;
import com.cse.p2p.parser.*;
import java.util.ArrayList;
import java.util.HashMap;
import server.dicfile.DicFileGenerating;
import server.meta.UploadMeta;
import serverside.DataServerSide;

/**
 *
 * @author TuyetNgan
 */
public class ServerControlTask_Server extends Thread {

	private DataServerSide dataThread;
	private Socket socket = null;
	private PrintStream out = null;
	private BufferedReader in = null;
	private BufferedReader infromScreen = null;
	private FileMetadata file_mete_data;
	private DicFileGenerating dic;
	private HashMap<String, String> peerOnline;
	private volatile boolean isWaiting;

	public ServerControlTask_Server( Socket s, HashMap st, DataServerSide dThread ) {
		this.dataThread = dThread;
		this.socket = s;
		this.peerOnline = st;
	}

	public void newChat() {
		try {
			out = new PrintStream( socket.getOutputStream() );
			in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
			infromScreen = new BufferedReader( new InputStreamReader( System.in ) );
		}
		catch ( IOException e ) {
		}
	}

	public void closeConnection() {
		try {
			out.close();
			in.close();
			infromScreen.close();
			socket.close();
		}
		catch ( IOException e ) {
		}

	}

	@Override
	public void run() {
		try {
			newChat();
			System.out.println( "NETWORK: CONTROL: New client connected: " + socket.getRemoteSocketAddress().toString() );
			String mess;

			while ( ( mess = in.readLine() ) != null ) {
				System.out.println( "NETWORK: CONTROL: Got a new message" );
				Message message = Message.fromString( mess );
				int typeOfProtocol = message.getType();
				String data = message.getData();
				switch ( typeOfProtocol ) {

					// client register first
					case Message.MsgType.REQ_REG_NICKIN: {
						System.out.println( "NETWORK: CONTROL: New registration request from " + socket.getRemoteSocketAddress() );
						String userName = Integer.toString( peerOnline.size() );
						String tmpRemoteIP = this.socket.getRemoteSocketAddress().toString();
						peerOnline.put( userName, tmpRemoteIP.substring( 1, tmpRemoteIP.lastIndexOf( ":" ) ) );
						Message responsePeer = new Message( "PUT", "SIGN" );
						out.println( responsePeer.toString() );
						System.out.println( "PeerOnline " + userName );
						break;
					}
					// client sign out 
					case Message.MsgType.REQ_REG_NICKOUT: {
						System.out.println( "NETWORK: CONTROL: The user at " + socket.getRemoteSocketAddress() + " logged out" );
						String userName = data;
						// remove all instant of an IP address 
						String IPsignout = this.socket.getRemoteSocketAddress().toString();
						for ( String str : peerOnline.keySet() ) {
							if ( IPsignout.substring( 1, IPsignout.lastIndexOf( ":" ) ).equals( peerOnline.get( str ) ) ) {
								peerOnline.remove( str );
							}
							else {
								System.out.println( "\t" + peerOnline.get( str ) );
							}
						}


						// end of remove
						System.out.println( "PeerOffline " + userName );
						break;
					}
					// Client -> Server: get the list of all available pieces of a file
					case Message.MsgType.REQ_FOR_AVAI_PIECES: {
						System.out.println( "NETWORK: CONTROL: Request all piece of file '" + data + "' from " + socket.getRemoteSocketAddress() );
						System.err.println( "INFO: List: Online peers" );
						for ( String string : peerOnline.keySet() ) {
							System.out.println( "\t" + peerOnline.get( string ) );
						}
						int fileID = Integer.parseInt( data );
						// call thread peer to collect info -> fileID
						ArrayList<PeerInfo> peerInfoList = new ArrayList<PeerInfo>();
						String currentIP = this.socket.getRemoteSocketAddress().toString().substring( 1, this.socket.getRemoteSocketAddress().toString().lastIndexOf( ":" ) );
						System.out.println("Current client IP " +  currentIP);
						for ( String string : peerOnline.keySet() ) {
							String IPadd = peerOnline.get( string );

							if ( !IPadd.equals( currentIP ) ) {
								ServerControlTask_Client t = new ServerControlTask_Client( this, IPadd, CommonSettings.CLIENT_SERVERSOCKET, fileID );
								t.start();
								this.isWaiting = false;

								while ( !this.isWaiting ) {
								}
								if ( t.getData() != null ) {
									peerInfoList.add( t.getData() );
								}
							}
						}
						/* concatenate all peerInfo from mant peer into a list and send to client request */
						System.out.println( "NETWORK: COMPLETE COLLECTING: Sending to client " + socket.getRemoteSocketAddress() );
						Message message1 = new Message( Message.Command.PUT, Message.Keyword.LIST, peerInfoList.toString() );
						out.println( message1 );
						break;
					}

					// client ask for the permision of uploading a file
					case Message.MsgType.REQ_UP_HOST_FILE: {
						System.out.println( "NETWORK: CONTROL: " + socket.getRemoteSocketAddress() + " request to upload a new file!" );
						file_mete_data = FileMetadata.fromString( data );
						/* analyzing file */
						// Call LocalFile to check Disk
						String currentPath = ( new File( "." ) ).getAbsolutePath();
						String defaultFolder = currentPath + File.separatorChar + "storage";

						File defaultStorageDir = new File( defaultFolder );
						if ( !defaultStorageDir.exists() ) {
							defaultStorageDir.mkdir();
						}

						File newFile = new File( defaultFolder + File.separatorChar + file_mete_data.getFileName() + "-" + Long.toString( System.currentTimeMillis() / 1000 ) );
						System.out.println( "New file will be created as " + newFile.getAbsolutePath() );

						out.println( ( new Message( Message.Command.PUT, Message.Keyword.UP, "OK" ) ).toString() );
						out.flush();

						String remoteAddr = socket.getRemoteSocketAddress().toString();
						dataThread.registerNewUpload( remoteAddr.substring( 1, remoteAddr.lastIndexOf( ":" ) ),
													  new UploadMeta( newFile.getAbsolutePath(), file_mete_data, this ) );

						break;
					}

					// client response receive message upload file is finished
					case Message.MsgType.RES_UP_DIC_FILE: {
						if ( data.equals( "OK" ) ) {
							System.out.println( "NETWORK: CONTROL: Client have got the DicFile" );
							System.out.println( "Complete the Dicfile generating routine with " + socket.getRemoteSocketAddress().toString() );
						}
						else if ( data.equals( "NONE" ) ) {
							// never happen this case
							System.out.println( "NETWORK: CONTROL: DicFile transfer failed" );

						}
						else { // error
							System.out.println( "ERROR : RES_UP_DIC_FILE" );
						}
						break;
					}

					// client response for the dicfile transfering
					case Message.MsgType.RES_FILE_TRANSFER: {
						System.out.println( "Haha" );
						if ( data.equals( "OK" ) ) {
							System.out.print( "Finish!" );
						}
						else if ( data.equals( "NONE" ) ) {
							/* remember client didnot receive dicfile */
							/* if client online , send again*/

							/* send and wait for signal from client accept DicFile */
							Message messSend2 = new Message( "GET", "UP", "DIC" );
							out.println( messSend2.toString() );
						}
						else {
							System.out.println( "ERROR : RES_FILE_TRANSFER" );
						}
						break;
					}


				}
			}
		}
		catch ( Exception exc ) {
			System.err.println( exc.getMessage() );
			exc.printStackTrace();
		}
	}

	public void sendDicFile( DicFile dicFile ) {
		System.out.println( "Sending Dicfile to " + socket.getRemoteSocketAddress().toString() );
		out.println( ( new Message( Message.Command.GET, Message.Keyword.UP, "DIC" ) ).toString() );
		out.flush();
		try {
			PrintStream ps = new PrintStream( socket.getOutputStream() );
			String XML = DicFile.XMLFromDicFile( dicFile );
			ps.println( XML );
//			sr.close();
		}
		catch ( Exception e ) {
			e.printStackTrace();
		}
	}

	public synchronized void notifyTimeOut() {
		this.isWaiting = true;
	}

	private class WaitingClass extends Thread {

		private ServerControlTask_Server server;

		public WaitingClass( ServerControlTask_Server server ) {
			this.server = server;
		}

		@Override
		public void run() {
			try {
				sleep( 500 );
				this.server.notifyTimeOut();
			}
			catch ( InterruptedException ex ) {
				System.out.println( "ERROR: Thread: Interupted" );
				ex.printStackTrace();
			}
		}
	}
}
